package ginutil

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"mime"
	"net/http"
	"net/url"
	"os"
	"path/filepath"
	"strconv"
	"strings"
	"sync"

	"gitee.com/tomatomeatman/golang-repository/bricks/model/fileback"
	"gitee.com/tomatomeatman/golang-repository/bricks/model/msgentity"
	Log "github.com/cihub/seelog"
	"github.com/gin-contrib/static"
	"github.com/gin-gonic/gin"
	"github.com/gorilla/websocket"
	"gopkg.in/ini.v1"
)

type Context = *gin.Context

var SetMode = gin.SetMode
var Default = gin.Default

const DebugMode = gin.DebugMode
const ReleaseMode = gin.ReleaseMode
const TestMode = gin.TestMode

// 请求类型枚举
type RequestType string

const (
	GET       RequestType = "GET"
	POST      RequestType = "POST"
	DELETE    RequestType = "DELETE"
	PUT       RequestType = "PUT"
	OPTIONS   RequestType = "OPTIONS"
	WEBSOCKET RequestType = "WEBSOCKET"
)

// 枚举转字符串
func (rt RequestType) String() string {
	switch rt {
	case GET:
		return "GET"
	case POST:
		return "POST"
	case DELETE:
		return "DELETE"
	case PUT:
		return "PUT"
	case OPTIONS:
		return "OPTIONS"
	case WEBSOCKET:
		return "WEBSOCKET"
	default:
		return "NA"
	}
}

var (
	regeditWaitGroup sync.WaitGroup                    //同步原语,要保证所有的Url注册都已经完成
	writeLock        sync.Mutex                        //保存锁
	httpHandleFunc   = make(map[string]HttpHandleInfo) //http控制层接口信息集合
	websocketFunc    = make(map[string]HttpHandleInfo) //websocket控制层接口信息集合
	appGinServer     *http.Server

	aopBefore = make(map[string][]func(ctx Context, params ...interface{}) *msgentity.MsgEntity) //前置拦截器
	aopAround = make(map[string][]func(ctx Context, params ...interface{}) *msgentity.MsgEntity) //环绕拦截器
	aopAfter  = make(map[string][]func(ctx Context, params ...interface{}) *msgentity.MsgEntity) //后置拦截器
)

// http控制层接口信息
type HttpHandleInfo struct {
	Url  string
	Type RequestType
	Fun  func(ctx Context) interface{}
}

// 设置跨域
func SetCors(r *gin.Engine) {
	r.Use(Cors())
}

func GetGin(ginMode, webRoot, Port string, InterceptorFunc func(ctx Context) bool) *http.Server {
	HttpHandleInfos := GetController()

	switch strings.ToUpper(ginMode) { //gin运行模式
	case "RELEASE":
		gin.SetMode(gin.ReleaseMode)
	case "TEST":
		gin.SetMode(gin.TestMode)
	case "DEBUG":
		gin.SetMode(gin.DebugMode)
	default:
		gin.SetMode(gin.ReleaseMode)
	}

	r := gin.Default()

	SetCors(r)                //设置跨域
	SetStatic(r, "."+webRoot) //设置静态目录

	r.GET("/shutdown", Shutdown)        //关机接口
	r.POST("/shutdown", Shutdown)       //关机接口
	r.GET("/websocket", WebsocketServe) //websocket接口

	for _, val := range HttpHandleInfos {
		switch val.Type {
		case GET:
			r.GET(val.Url, GetHandleFunc(val.Url, GET, InterceptorFunc, HttpHandleInfos))
		case POST:
			r.POST(val.Url, GetHandleFunc(val.Url, POST, InterceptorFunc, HttpHandleInfos))
		case DELETE:
			r.DELETE(val.Url, GetHandleFunc(val.Url, DELETE, InterceptorFunc, HttpHandleInfos))
		case PUT:
			r.PUT(val.Url, GetHandleFunc(val.Url, PUT, InterceptorFunc, HttpHandleInfos))
		case OPTIONS:
			r.OPTIONS(val.Url, GetHandleFunc(val.Url, OPTIONS, InterceptorFunc, HttpHandleInfos))
		// case WEBSOCKET:
		// 	r.GET(val.Url, GetHandleWebsocketFunc(val.Url, WEBSOCKET, InterceptorFunc, HttpHandleInfos))
		default:
			r.GET(val.Url, func(ctx Context) {
				ctx.JSONP(http.StatusBadRequest, msgentity.Err(1001, "请求方式错误,不支持此方式"))
			})
		}
	}

	httpHandleFunc = make(map[string]HttpHandleInfo) //清理

	appGinServer = &http.Server{
		Addr:    ":" + Port,
		Handler: r,
	}

	return appGinServer
}

// 跨域函数
func Cors() gin.HandlerFunc {
	return func(c *gin.Context) {
		method := c.Request.Method
		origin := c.Request.Header.Get("Origin")
		if origin != "" {
			c.Header("Access-Control-Allow-Origin", "*") // 可将将 * 替换为指定的域名
			c.Header("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE, UPDATE")
			//c.Header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept, Authorization")
			//c.Header("Access-Control-Expose-Headers", "Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers, Cache-Control, Content-Language, Content-Type")
			c.Header("Access-Control-Allow-Headers", "*")
			c.Header("Access-Control-Expose-Headers", "*")
			c.Header("Access-Control-Allow-Credentials", "true")
		}
		if method == "OPTIONS" {
			c.AbortWithStatus(http.StatusNoContent)
		}
		c.Next()
	}
}

// 设置静态路径
func SetStatic(r *gin.Engine, webroot string) {
	_ = mime.AddExtensionType(".js", "application/javascript") // 特殊处理JS文件的Content-Type
	r.Use(static.Serve("/", static.LocalFile(webroot, true)))  // 前端项目静态资源
}

// 注册web接口
func RegisterController(url string, iTypes interface{}, handler func(ctx Context) interface{}) {
	RegisterControllers(url, iTypes, handler)
}

// 注册web接口
func ControllerRegister(url string, handler func(ctx Context) interface{}, iTypes ...RequestType) {
	if len(iTypes) < 1 {
		registerController(url, POST, handler)
		return
	}

	for _, rt := range iTypes {
		registerController(url, rt, handler)
	}
}

// 注册web接口
func RegisterControllers(url string, iTypes interface{}, handler func(ctx Context) interface{}) {
	switch t := iTypes.(type) {
	case RequestType:
		registerController(url, t, handler)
	case []RequestType:
		if len(iTypes.([]RequestType)) < 1 {
			registerController(url, POST, handler)
			return
		}

		for _, v := range iTypes.([]RequestType) {
			registerController(url, v, handler)
		}
	default:
		registerController(url, POST, handler)
	}
}

// 注册web接口
func registerController(url string, iType RequestType, handler func(ctx Context) interface{}) {
	regeditWaitGroup.Add(1)       // 注册前增加计数器
	defer regeditWaitGroup.Done() // 注册结束时减少计数器

	writeLock.Lock()         //加锁
	defer writeLock.Unlock() //解锁

	temp := url + iType.String()
	_, ok := httpHandleFunc[temp]
	if ok {
		Log.Error("链接重复:" + url)
		return
	}

	if iType == WEBSOCKET {
		websocketFunc[url] = HttpHandleInfo{
			Url:  url,
			Type: iType,
			Fun:  handler,
		}

		return
	}

	httpHandleFunc[temp] = HttpHandleInfo{
		Url:  url,
		Type: iType,
		Fun:  handler,
	}
}

/**
 * 根据url取函数
 * @param url
 * @param iType
 * @param ctx
 * @return
 */
func CallFuncByUrl(url string, iType RequestType, ctx Context) interface{} {
	temp := url + iType.String()
	fun, ok := httpHandleFunc[temp]
	if !ok {
		return nil
	}

	return fun.Fun(ctx)
}

// 取注册的web接口
func GetController() map[string]HttpHandleInfo {
	regeditWaitGroup.Wait() // 等待所有注册完成
	return httpHandleFunc
}

/**
 * 取注册的web接口
 */
func GetHandleFunc(urlKey string, iType RequestType, InterceptorFunc func(ctx Context) bool,
	controllerMap map[string]HttpHandleInfo) func(ctx Context) {
	result := func(ctx Context) {
		if nil != InterceptorFunc {
			if !InterceptorFunc(ctx) {
				return
			}
		}

		urlStr := ctx.Request.URL.Path

		var urlAndType string
		if strings.HasPrefix(urlStr, "/proxy/") { //路径中存在转发请求
			urlAndType = "/proxy/*proxy" + iType.String()
		} else {
			urlAndType = urlKey + iType.String() //url和请求方式组合
		}

		httpHandleInfo, ok := controllerMap[urlAndType] //给个默认
		if !ok {
			return
		}

		// 执行前置函数
		meBefore := CallBeforeFunc(urlStr, ctx)
		if !meBefore.Gsuccess {
			ctx.JSONP(http.StatusOK, meBefore)
			return
		}

		// 执行本体函数
		obj := httpHandleInfo.Fun(ctx)
		if obj == nil { //如果返回值是nil，则直接返回(需要函数内自己处理)
			return
		}

		// 执行后置函数
		meAfter := CallAfterFunc(urlStr, ctx, obj)
		if !meAfter.Gsuccess {
			ctx.JSONP(http.StatusOK, meAfter)
			return
		}

		// 执行返回处理
		switch obj := obj.(type) {
		case *msgentity.MsgEntity, msgentity.MsgEntity:
			ctx.JSONP(http.StatusOK, obj)
		case *fileback.FileBack:
			fm := obj
			if !fm.Gsuccess {
				ctx.JSONP(http.StatusOK, obj)
				break
			}

			if fm.Gdisposition != "" {
				ctx.Header("Content-Disposition", fm.Gdisposition)
			}

			ctx.Header("Content-Type", fm.Gtype)
			ctx.Header("Content-Length", fm.GsLength)
			ctx.Writer.Write(fm.Gdata.([]byte))
		case fileback.FileBack:
			fm := obj
			if !fm.Gsuccess {
				ctx.JSONP(http.StatusOK, obj)
				break
			}

			if fm.Gdisposition != "" {
				ctx.Header("Content-Disposition", fm.Gdisposition)
			}

			ctx.Header("Content-Type", fm.Gtype)
			ctx.Header("Content-Length", fm.GsLength)
			ctx.Writer.Write(fm.Gdata.([]byte))
		case []byte:
			by := obj
			ctx.Header("Content-Type", "application/octet-stream")
			ctx.Header("Content-Length", strconv.Itoa(len(by)))
			ctx.Writer.Write(by)
		default:
			ctx.Writer.Write([]byte(fmt.Sprintf("%v", obj)))
		}
	}

	return result
}

/**
 * 取注册的Websocket接口
 */
func GetHandleWebsocketFunc(urlKey string, iType RequestType, InterceptorFunc func(ctx Context) bool,
	controllerMap map[string]HttpHandleInfo) func(ctx Context) {

	result := func(ctx Context) {
		if nil != InterceptorFunc {
			if !InterceptorFunc(ctx) {
				return
			}
		}

		var upgrader = websocket.Upgrader{
			// 这个是校验请求来源
			// 在这里我们不做校验，直接return true
			CheckOrigin: func(r *http.Request) bool {
				return true
			},
		}

		// 将普通的http GET请求升级为websocket请求
		client, err := upgrader.Upgrade(ctx.Writer, ctx.Request, nil)
		if err != nil {
			ctx.JSON(http.StatusInternalServerError, gin.H{"error": "将普通的http GET请求升级为websocket请求失败"})
			return
		}
		defer client.Close()

		urlAndType := urlKey + iType.String() //url和请求方式组合

		httpHandleInfo, ok := controllerMap[urlAndType] //给个默认
		if !ok {
			return
		}

		for {
			_, value, err := client.ReadMessage()
			if err != nil {
				return
			}

			// 将信息存放到请求参数中
			r := ctx.Request

			r.ParseForm() //警告:必须先 解析所有请求数据

			form := r.Form
			if form == nil {
				form = make(url.Values)
				r.Form = form
			}

			form.Set("msg", string(value))

			// 执行本体函数
			obj := httpHandleInfo.Fun(ctx)
			if obj == nil { //如果返回值是nil，则直接返回(需要函数内自己处理)
				msg := msgentity.ErrString(1001, "执行失败,执行结果为nil")
				err = client.WriteMessage(websocket.TextMessage, []byte(msg))
				if err != nil {
					Log.Error("Websocket发送信息到前端异常:", err)
					return
				}

				return
			}

			// 发送执行结果
			msg, _ := json.Marshal(obj)
			err = client.WriteMessage(websocket.TextMessage, msg)
			if err != nil {
				Log.Error("Websocket发送信息到前端异常:", err)
				return
			}
		}
	}

	return result
}

// 关闭服务
func Shutdown(ctx Context) {
	key := getParamString(ctx, "sInsideKey", "")
	if key == "" {
		ctx.JSONP(http.StatusOK, msgentity.Err(100001, "关闭密钥参数sInsideKey缺失"))
		return
	}

	appKey := readConfigKey("App", "InsideKey", "12345")
	if appKey != key {
		ctx.JSONP(http.StatusOK, msgentity.Err(100001, "关闭密钥参数错误"))
		return
	}

	Log.Debug("关闭服务...")

	if err := appGinServer.Shutdown(ctx); err != nil {
		Log.Debug("服务强制关闭发生异常:", err)
		ctx.JSONP(http.StatusOK, msgentity.Err(100003, "服务强制关闭发生异常"))
		return
	}

	fmt.Println("服务关闭")
	ctx.JSONP(http.StatusOK, msgentity.Success(9999, "服务强制关闭成功"))
}

/**
 * 取参数
 * ctx GinHttp上下文对象
 * name 参数名称
 * def 默认值
 */
func getParamString(ctx Context, name, def string) string {
	ctx.Request.ParseForm() //警告:必须先 解析所有请求数据

	//-- 取POST方法的参数 --//
	params := make(map[string]interface{})

	br, _ := io.ReadAll(ctx.Request.Body)
	ctx.Request.Body.Close()
	ctx.Request.Body = io.NopCloser(bytes.NewBuffer(br))

	json.NewDecoder(bytes.NewBuffer(br)).Decode(&params)
	temp, ok := params[name]
	if ok {
		return fmt.Sprint(temp)
	}

	//-- 取GET方法的参数 --//
	query := ctx.Request.URL.Query() // 获取请求的参数

	v := query[name]
	if len(v) > 0 && strings.TrimSpace(v[0]) != "" {
		return v[0]
	}

	return def
}

/**
 * 读取配置文件
 * section 配置文件section
 * key 配置文件key
 * def 默认值
 */
func readConfigKey(section, key, def string) string {
	root := ""
	exePath, err := os.Executable()
	if err != nil {
		root = "."
	}

	root, _ = filepath.EvalSymlinks(filepath.Dir(exePath))

	configFilePath := strings.Replace(root+"/config/app.ini", "\\", "/", -1)

	_, err = os.Stat(configFilePath) //os.Stat获取文件信息
	if err != nil {
		if !os.IsExist(err) {
			Log.Error("配置文件不存在", err)
			return def
		}
	}

	// 加载配置文件
	conf, err := ini.Load(configFilePath)
	if err != nil {
		Log.Error("读取配置文件失败:", err)
		return def
	}

	// 获取配置文件中的 section 和 key
	appSection := conf.Section(section)
	result, err := appSection.GetKey(key)
	if err != nil {
		Log.Error("读取配置项失败:", err)
		return def
	}

	return fmt.Sprintf("%v", result)
}

/**
 * 注册Aop-函数执行前调用函数
 * @param urlStr 被监听函数
 * @param doFunc 被调用函数
 * @return
 */
func RegisterBefore(urlStr string, doFunc func(ctx Context, params ...interface{}) *msgentity.MsgEntity) {
	urlStr = strings.TrimSpace(urlStr)
	if urlStr == "" {
		return
	}

	regeditWaitGroup.Add(1)       // 注册前增加计数器
	defer regeditWaitGroup.Done() // 注册结束时减少计数器

	writeLock.Lock()         //加锁
	defer writeLock.Unlock() //解锁

	funcArray, ok := aopBefore[urlStr]
	if ok {
		funcArray = append(funcArray, doFunc)
		aopBefore[urlStr] = funcArray
		return
	}

	funcArray = []func(ctx Context, params ...interface{}) *msgentity.MsgEntity{doFunc}
	aopBefore[urlStr] = funcArray
}

/**
 * 注册Aop-控制函数执行后调用函数
 * @param urlStr 被监听控制Url地址(注册web接口时的url地址)
 * @param doFunc 被调用函数
 * @return
 */
func RegisterAfter(urlStr string, doFunc func(ctx Context, params ...interface{}) *msgentity.MsgEntity) {
	urlStr = strings.TrimSpace(urlStr)
	if urlStr == "" {
		return
	}

	regeditWaitGroup.Add(1)       // 注册前增加计数器
	defer regeditWaitGroup.Done() // 注册结束时减少计数器

	writeLock.Lock()         //加锁
	defer writeLock.Unlock() //解锁

	funcArray, ok := aopAfter[urlStr]
	if ok {
		funcArray = append(funcArray, doFunc)
		aopAfter[urlStr] = funcArray
		return
	}

	funcArray = []func(ctx Context, params ...interface{}) *msgentity.MsgEntity{doFunc}
	aopAfter[urlStr] = funcArray
}

/**
 * 注册Aop-控制函数执行中调用函数
 * @param urlStr 被监听控制Url地址(注册web接口时的url地址)
 * @param doFunc 被调用函数
 * @return
 */
func RegisterAround(urlStr string, doFunc func(ctx Context, params ...interface{}) *msgentity.MsgEntity) {
	urlStr = strings.TrimSpace(urlStr)
	if urlStr == "" {
		return
	}

	regeditWaitGroup.Add(1)       // 注册前增加计数器
	defer regeditWaitGroup.Done() // 注册结束时减少计数器

	writeLock.Lock()         //加锁
	defer writeLock.Unlock() //解锁

	funcArray, ok := aopAround[urlStr]
	if ok {
		funcArray = append(funcArray, doFunc)
		aopAround[urlStr] = funcArray
		return
	}

	funcArray = []func(ctx Context, params ...interface{}) *msgentity.MsgEntity{doFunc}
	aopAround[urlStr] = funcArray
}

/**
 * 调用Aop-控制函数执行前调用函数
 * @param urlStr 被监听控制Url地址(注册web接口时的url地址)
 * @param doFunc 被调用函数
 * @return
 */
func CallBeforeFunc(urlStr string, ctx Context, params ...interface{}) *msgentity.MsgEntity {
	return callFunc(aopBefore, urlStr, ctx, params)
}

/**
 * 调用Aop-函数执行后调用函数
 * @param urlStr 被监听控制Url地址(注册web接口时的url地址)
 * @param doFunc 被调用函数
 * @return
 */
func CallAfterFunc(urlStr string, ctx Context, params ...interface{}) *msgentity.MsgEntity {
	return callFunc(aopAfter, urlStr, ctx, params)
}

/**
 * 调用Aop-函数执行中调用函数
 * @param urlStr 被监听控制Url地址(注册web接口时的url地址)
 * @param doFunc 被调用函数
 * @return
 */
func CallAroundFunc(urlStr string, ctx Context, params ...interface{}) *msgentity.MsgEntity {
	return callFunc(aopAround, urlStr, ctx, params)
}

/**
 * 调用Aop-函数执行中调用函数
 * @param aopInfo aop信息
 * @param urlStr 被监听控制Url地址(注册web接口时的url地址)
 * @param doFunc 被调用函数
 * @return
 */
func callFunc(aopInfo map[string][]func(ctx Context, params ...interface{}) *msgentity.MsgEntity,
	urlStr string, ctx Context, params []interface{}) *msgentity.MsgEntity {
	if urlStr == "" {
		return msgentity.Success(1000, "函数名为空,不处理")
	}

	funcArray, ok := aopInfo[urlStr]
	if !ok {
		return msgentity.Success(1001, "没有函数,不处理")
	}

	if len(funcArray) < 1 {
		return msgentity.Success(1002, "没有调用函数,结束AOP处理")
	}

	for _, fun := range funcArray {
		me := fun(ctx, params...)
		if !me.Gsuccess {
			return me
		}
	}

	return msgentity.Success(1003, "调用函数没有错误,结束AOP处理")
}

/**
 * websocket服务
 * @param context
 */
func WebsocketServe(context Context) {
	var upgrader = websocket.Upgrader{
		// 这个是校验请求来源
		// 在这里我们不做校验，直接return true
		CheckOrigin: func(r *http.Request) bool {
			return true
		},
	}

	// 将普通的http GET请求升级为websocket请求
	client, err := upgrader.Upgrade(context.Writer, context.Request, nil)
	if err != nil {
		context.JSON(http.StatusInternalServerError, gin.H{"error": "Upgrade failed"})
		return
	}
	defer client.Close()

	for {
		_, jsonStr, err := client.ReadMessage()
		if err != nil {
			return
		}

		m := make(map[string]interface{})
		err = json.Unmarshal([]byte(jsonStr), &m)
		if err != nil {
			Log.Error("Json字符串转换异常: %+v\n", err)

			me := map[string]interface{}{
				"requestSign": "未知",
				"success":     false,
				"msg":         "请求失败,请求参数格式可能存在问题,必须是json字符串格式:{'controllerurl':'控制层url','参数1':'参数1值','参数2':参数2值}",
				"data":        1001,
			}

			result, _ := json.Marshal(me)
			err = client.WriteMessage(websocket.TextMessage, result)
			if err != nil {
				Log.Error("Websocket发送信息到前端异常:", err)
				return
			}

			return
		}

		sign := m["requestSign"]

		urlstr, ok := m["controllerurl"]
		if !ok {
			me := map[string]interface{}{
				"requestSign": sign,
				"success":     false,
				"msg":         "请求失败,controllerurl参数缺失",
				"data":        1002,
			}

			result, _ := json.Marshal(me)
			err = client.WriteMessage(websocket.TextMessage, result)
			if err != nil {
				Log.Error("Websocket发送信息到前端异常:", err)
				return
			}

			return
		}

		httpHandleInfo, ok := websocketFunc[urlstr.(string)] //给个默认
		if !ok {
			me := map[string]interface{}{
				"requestSign": sign,
				"success":     false,
				"msg":         "没有符合的请求接口",
				"data":        1003,
			}

			result, _ := json.Marshal(me)
			err = client.WriteMessage(websocket.TextMessage, result)
			if err != nil {
				Log.Error("Websocket发送信息到前端异常:", err)
				return
			}

			return
		}

		// 将信息存放到请求参数中
		r := context.Request

		r.ParseForm() //警告:必须先 解析所有请求数据

		form := r.Form
		if form == nil {
			form = make(url.Values)
			r.Form = form
		}

		for k, v := range m {
			if k == "controllerurl" {
				continue
			}

			if k == "requestSign" {
				continue
			}

			form.Set(k, fmt.Sprintf("%v", v))
		}

		// 执行本体函数
		obj := httpHandleInfo.Fun(context)
		if obj == nil { //如果返回值是nil，则直接返回(需要函数内自己处理)
			me := map[string]interface{}{
				"requestSign": sign,
				"success":     false,
				"msg":         "执行失败,执行结果为nil",
				"data":        1004,
			}

			result, _ := json.Marshal(me)
			err = client.WriteMessage(websocket.TextMessage, result)
			if err != nil {
				Log.Error("Websocket发送信息到前端异常:", err)
				return
			}

			return
		}

		// 发送执行结果
		temp := obj.(*msgentity.MsgEntity)

		me := map[string]interface{}{
			"requestSign": sign,
			"success":     temp.Gsuccess,
			"msg":         temp.Gmsg,
			"data":        temp.Gdata,
		}

		result, _ := json.Marshal(me)
		err = client.WriteMessage(websocket.TextMessage, result)
		if err != nil {
			Log.Error("Websocket发送信息到前端异常:", err)
			return
		}
	}
}
